Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use newer vega versions for altair compatibility #959

Merged

Conversation

JarnoRFB
Copy link
Contributor

Newer versions of vega, vega-lite and vega-embed are used to allow the
usage of altair 2.x. Additionally,
the JavaScript for embedding is adapted. jsdelivr is used instead of
cloudflare, since it allows to specify the newest minor version very
nicely and it is recommended by the vega-lite docs
https://vega.github.io/vega-lite/usage/embed.html#cdn

Newer versions of vega, vega-lite and vega-embed are used. Additionally,
the JavaScript for embedding is adapted. jsdelivr is used instead of
cloudflare, since it allows to specify the newest minor version very
nicely and it is recommended by the vega-lite docs
https://vega.github.io/vega-lite/usage/embed.html#cdn
@JarnoRFB
Copy link
Contributor Author

Hi folium team,
just stumbled upon the old vega version, that caused plots to not work with altair 2.x. If you like the idea of updating, than this feature could probably use some tests.

Copy link
Member

@Conengmo Conengmo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate your PR, it is good to keep up to date with these libraries. But since the change from Vega 2 to 3 (and Vega Lite 1 to 2 also?) is not backwards compatible, I would like to give users the option to keep using the previous version. What do you think would be the best way to implement this?

@@ -232,7 +232,7 @@ class VegaLite(Element):

def __init__(self, data, width=None, height=None,
left='0%', top='0%', position='relative'):
super(VegaLite, self).__init__()
super(self.__class__, self).__init__()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this PR clean and don't change this here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somehow got a strange error at some point, when I used VegaLite instead of self.__class__, but I can revert that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the error?

name='d3')

figure.header.add_child(
JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/vega/2.6.5/vega.min.js'), # noqa
JavascriptLink('https://cdn.jsdelivr.net/npm/vega@3'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this kind of cdn url

{{this.get_name()}}, embedSpec, function(error, result) {}
);
const spec = {{this.json}};
vegaEmbed({{this.get_name()}}, spec)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems vegaEmbed is the function to use in vega embed version 3, while vg.embed was used in version 2, is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that seems to be the case.

@@ -232,7 +232,7 @@ class VegaLite(Element):

def __init__(self, data, width=None, height=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're only updating VegaLite it seems, why not Vega also?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, will do

@JarnoRFB
Copy link
Contributor Author

Thanks for the review! I also thought about leaving support for VegaLite 1, but when I tried it out, it seemed to be backwards compatible at least for a simple examples and I first wanted to find out if this feature is desired at all. I think the easiest way to keep backwards compatible support would be to query the $schema field of the vega lite spec. I will add this soon when I find the time.

@Conengmo
Copy link
Member

I think the easiest way to keep backwards compatible support would be to query the $schema field of the vega lite spec

Sounds like a good idea. If someone specifies the $schema field, use that version, else use the most recent version. We do have to mention that in the docs though.

I was thinking about the best place to do the switching. Maybe in the render method of the Vega and VegaLite classes. I hope you can find a way to implement the necessary if switching statements while keep the code clean.

I will add this soon when I find the time.

Thanks! Looking forward to improving our Vega (Lite) support.

@Conengmo Conengmo added the waiting for changes This PR has been reviewed and changes are needed before merging label Oct 15, 2018
@JarnoRFB
Copy link
Contributor Author

I finally found time to do this. Let me know what you think of the code, I will add a section describing the changes to the docs then. I now implemented this only for vegalite, as this is relevant for altair. I would prefer to take care of vega in a separate PR.

@Conengmo Conengmo added waiting for review PR is waiting to be reviewed and removed waiting for changes This PR has been reviewed and changes are needed before merging labels Nov 25, 2018
@@ -112,7 +113,8 @@ def render(self, **kwargs):
'if it is not in a Figure.')

figure.header.add_child(
JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-dvf/0.3.0/leaflet-dvf.markers.min.js'), # noqa
JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-dvf/0.3.0/leaflet-dvf.markers.min.js'),
# noqa
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this all in one line.

@@ -208,6 +210,10 @@ def render(self, **kwargs):
name='vega_parse')


def get_vega_versions(spec):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used somewhere?

@ocefpaf
Copy link
Member

ocefpaf commented Dec 11, 2018

Something is not OK with the background, this is our notebook example with this PR:

screenshot from 2018-12-11 16-18-58

@ocefpaf ocefpaf mentioned this pull request Dec 11, 2018
@JarnoRFB
Copy link
Contributor Author

Thanks for the review! I incorporated the comments. Regarding the background issue, I will probably wait until #1040 is merged. Then it might work as intended.

@ocefpaf
Copy link
Member

ocefpaf commented Dec 12, 2018

Thanks for the review! I incorporated the comments. Regarding the background issue, I will probably wait until #1040 is merged. Then it might work as intended.

Yep. That is not caused by this PR.

def _get_vegalite_major_versions(self, spec):
try:
schema = spec['$schema']
version = os.path.splitext(os.path.split(schema)[1])[0].lstrip('v')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need os to split schema here? I'm just thinking that we can avoid one extra import.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one could solve it just with str.split, but the os functions more direct and have somewhat speaking names. I also think importing os does not harm as it is in the standard library.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm +0.5 on that. Indeed os is standard lib, but it is an extra import that is used for a simple task. I would also argue that .split('/') is more readble than .path.splitext which is intended for file system paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convinced! Even needs one method call less this way.

except KeyError:
major_version = None
else:
major_version = schema.split('/')[-1].split('.')[0].lstrip('v')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1-line and less code 😄
Thanks!

Copy link
Member

@ocefpaf ocefpaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this one for @Conengmo to merge b/c he was the first reviewer, but LGTM.
Thanks @JarnoRFB for the awesome fix and support for multiple versions.


def _vega_embed(self):
self._parent.script.add_child(Element(Template("""
const spec = {{this.json}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec here will need a unique name or we won't be able to display more than 1 object.

Copy link
Contributor Author

@JarnoRFB JarnoRFB Dec 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a simple fix by just avoiding using the spec variable. However, we should test against this case for the future. Unfortunately I have no idea how to do this, as the error is happening on the JS side. Just executing a test with two VegaLite elements does not throw an error.
Putting an example in the example notebooks might be a way, but could look a bit awkward and is also not caught by pytest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a simple fix by just avoiding using the spec variable.

Great!

However, we should test against this case for the future. Unfortunately I have no idea how to do this, as the error is happening on the JS side. Just executing a test with two VegaLite elements does not throw an error.
Putting an example in the example notebooks might be a way, but could look a bit awkward and is also not caught by pytest.

Yeah, that is hard. We can create a map and compare an expected output but that may not catch errors like that. We do build all the maps in the example gallery and check them always (sometimes) before merging a PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe one could start a headless selenium browser and check if the map and the charts are there. But that would be maybe a bit two much for this PR. I can easily provide a notebook example for now and try to tackle more elaborate testing in a separate PR if that is fine for you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That kind of testing is in our TODO list for a long time and I agree, it is something for another PR.

This one is good to go, just waiting for @Conengmo last 👍 to merge it.

@Conengmo Conengmo merged commit 761cc3e into python-visualization:master Dec 23, 2018
@Conengmo
Copy link
Member

Thanks @JarnoRFB for your effort! And thanks @ocefpaf for the review.

Looking forward to the same for Vega!

@Conengmo Conengmo removed the waiting for review PR is waiting to be reviewed label Dec 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants